package uitl

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"
	qflog "github.com/qf-tech/qftech-base/pkg/log"
)

func CalculateHash(toBeHashed string) string {
	hashInBytes := sha256.Sum256([]byte(toBeHashed))
	hashStr := hex.EncodeToString(hashInBytes[:])
	return hashStr
}

func GenWsse(username string, password string, nonce string) string {
	created := time.Now().Format("2006-01-02T15:04:05Z")
	plainData := fmt.Sprintf("%s%s%s%s", username, password, nonce, created)
	digest := CalculateHash(plainData)

	wsseInfo := fmt.Sprintf("UsernameToken Username=\"%s\", PasswordDigest=\"%s\", Nonce=\"%s\", Created=\"%s\"",
		username, digest, nonce, created)
	return wsseInfo
}

func HttpPostJson(url string, reqBody interface{}, timeout time.Duration) (interface{}, error) {
	c := &http.Client{Timeout: timeout}

	// adminToken := os.Getenv("ADMIN_TOKEN")
	// req.Header.Add("ADMIN-TOKEN", adminToken)

	bytesData, err := json.Marshal(reqBody)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}
	reader := bytes.NewReader(bytesData)
	req, err := http.NewRequest("POST", url, reader)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var v interface{}
	err = json.Unmarshal(body, &v)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func HttpPostFile(filename string, targetURL string, headers map[string]string, timeout time.Duration) (interface{}, error) {
	qflog.Sugare.Infof("file: %s, url: %s, headers: %v", filename, targetURL, headers)
	bodyBuf := bytes.NewBufferString("")
	bodyWriter := multipart.NewWriter(bodyBuf)

	// use the bodyWriter to write the Part headers to the buffer
	_, err := bodyWriter.CreateFormFile("file", filename)
	if err != nil {
		qflog.Sugare.Error("error writing to buffer")
		return nil, err
	}

	// the file data will be the second part of the body
	fh, err := os.Open(filename)
	if err != nil {
		qflog.Sugare.Error("error opening file")
		return nil, err
	}
	// need to know the boundary to properly close the part myself.
	boundary := bodyWriter.Boundary()
	//close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
	closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))

	// use multi-reader to defer the reading of the file data until
	// writing to the socket buffer.
	requestReader := io.MultiReader(bodyBuf, fh, closeBuf)
	fi, err := fh.Stat()
	if err != nil {
		qflog.Sugare.Error("Error Stating file: %s", filename)
		return nil, err
	}

	req, err := http.NewRequest("POST", targetURL, requestReader)
	if err != nil {
		qflog.Sugare.Error("new request err: %v", err)
		return nil, err
	}

	// Set headers for multipart, and Content Length
	req.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	req.ContentLength = fi.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len())

	c := &http.Client{Timeout: timeout}
	resp, err := c.Do(req)
	if err != nil {
		qflog.Sugare.Error("do request err: %v", err)
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		qflog.Sugare.Error("read all from response err: %v", err)
		return nil, err
	}

	var v interface{}
	err = json.Unmarshal(body, &v)
	if err != nil {
		qflog.Sugare.Error("json unmarshal err: %v", err)
		return nil, err
	}

	return v, nil
}

func BaseName(path string) string {
	tmpPath := strings.Split(path, "?")[0]
	pos := strings.LastIndex(tmpPath, "/")
	return tmpPath[pos+1:]
}

func DownloadFile(url string, dstDir string, filename string) (string, error) {
	var res *http.Response
	var err error

	if strings.HasPrefix(url, "https://") {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} // 若需要测试自签名的证书 这里需设置跳过证书检测
		client := &http.Client{Transport: tr}
		res, err = client.Get(url)
	} else {
		res, err = http.Get(url)
	}
	if err != nil {
		return "", errors.Wrap(err, "get err")
	}
	defer res.Body.Close()

	if _, err := os.Stat(dstDir); os.IsNotExist(err) {
		_ = os.MkdirAll(dstDir, os.ModePerm)
	}

	reader := bufio.NewReaderSize(res.Body, 64*1024)

	// 优先使用filename，若为空，则使用 url 中 name
	if filename == "" {
		filename = BaseName(url)
	}
	filePath := dstDir + "/" + filename
	file, err := os.Create(filePath)
	if err != nil {
		return "", errors.Wrap(err, "create err")
	}

	writer := bufio.NewWriter(file)
	if _, err := io.Copy(writer, reader); err != nil {
		return "", errors.Wrap(err, "copy err")
	}

	if res.StatusCode != http.StatusOK {
		tmpMsg, _ := os.ReadFile(filePath)
		errMsg := strings.ReplaceAll(string(tmpMsg), "\n", ", ")
		return "", errors.New(errMsg)
	}

	return filePath, nil
}
